Skip to content

Reject serde-reserved keys in HITL params_input - #71054

Open
haseebmalik18 wants to merge 6 commits into
apache:mainfrom
haseebmalik18:hitl-reject-serde-reserved-param-keys
Open

Reject serde-reserved keys in HITL params_input#71054
haseebmalik18 wants to merge 6 commits into
apache:mainfrom
haseebmalik18:hitl-reject-serde-reserved-param-keys

Conversation

@haseebmalik18

@haseebmalik18 haseebmalik18 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

A HITL response whose params_input contains a serde-reserved key (__classname__ or __id__) cannot be serialized when the task resumes. Today the response is silently discarded and the user has no way to resubmit a corrected one.

This validates params_input when the response is submitted and returns a 400 if it carries one of those keys, before anything is written. The task stays in AWAITING_INPUT, so the user can fix the input and resubmit.

Only the public HITL route needs this. The execution API route builds its params_input from trusted operator code rather than user input, so it is left as is.

closes: #71036

@haseebmalik18 haseebmalik18 changed the title Reject serde-reserved keys in HITL params_input #71036 Reject serde-reserved keys in HITL params_input Aug 4, 2026
@eladkal eladkal added this to the Airflow 3.3.1 milestone Aug 4, 2026
@eladkal eladkal added type:bug-fix Changelog: Bug Fixes backport-to-v3-3-test Backport to v3-3-test labels Aug 4, 2026
@eladkal
eladkal requested a review from potiuk August 4, 2026 07:22
@vatsrahul1001
vatsrahul1001 requested a review from Lee-W August 4, 2026 07:52
@amoghrajesh

Copy link
Copy Markdown
Contributor

This doesn't have to be in 3.3.1, but if we are comfortable (cc @Lee-W) we can consider it.

Comment thread airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_hitl.py Outdated
@Lee-W

Lee-W commented Aug 4, 2026

Copy link
Copy Markdown
Member

A few nitpicks, but overall this looks good. I also don't think it's worth blocking the 3.3.1 release.

@vatsrahul1001

Copy link
Copy Markdown
Contributor

Moved to 3.3.2

@haseebmalik18

haseebmalik18 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

@Lee-W Applied changes left from review

@jason810496 jason810496 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this.

log = structlog.get_logger(__name__)


# Keys that ``serde.serialize`` refuses at any depth. A ``params_input`` carrying one could not be

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, we should follow the convetion in XCom and validate at the data model layer:

def _check_forbidden_xcom_keys(value: Any) -> Any:
"""Recursively reject forbidden deserialization keys in user-provided XCom data."""
from airflow._shared.serialization import FORBIDDEN_XCOM_KEYS
def _walk(obj: Any, path: str = "value") -> None:
if isinstance(obj, str):
# A value submitted as a JSON string literal (e.g. ``json.dumps({...})``)
# is stored verbatim and re-parsed into a dict/list on a
# ``deserialize=true`` read, which would otherwise smuggle reserved keys
# past the dict/list checks below. Re-parse and inspect the decoded
# structure the same way the read path does.
try:
decoded = json.loads(obj)
except (ValueError, TypeError):
return
if isinstance(decoded, (dict, list)):
_walk(decoded, path)
return
if isinstance(obj, dict):
found = FORBIDDEN_XCOM_KEYS & obj.keys()
if found:
raise ValueError(
f"XCom {path} contains reserved serialization keys: {', '.join(sorted(found))}. "
f"These keys are reserved for internal use."
)
for k, v in obj.items():
_walk(v, f"{path}.{k}")
elif isinstance(obj, (list, tuple)):
for i, item in enumerate(obj):
_walk(item, f"{path}[{i}]")
_walk(value)
return value

CLASSNAME = "__classname__"
VERSION = "__version__"
DATA = "__data__"
SCHEMA_ID = "__id__"
CACHE = "__cache__"
# Legacy serialization format keys (processed by serde for backwards compatibility)
OLD_TYPE = "__type"
OLD_SOURCE = "__source"
OLD_DATA = "__var"
OLD_DICT = "dict"
FORBIDDEN_XCOM_KEYS = frozenset(
{
CLASSNAME,
VERSION,
DATA,
SCHEMA_ID,
CACHE,
OLD_TYPE,
OLD_SOURCE,
OLD_DATA,
}
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:API Airflow's REST/HTTP API backport-to-v3-3-test Backport to v3-3-test type:bug-fix Changelog: Bug Fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HITL responses with serde-reserved keys in params_input cannot be resumed or resubmitted

6 participants